﻿using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading;
using System.Xml;

namespace ASoft.Led
{
    public class LedService
    {
        #region core

        string rootPath;
        readonly string ledCfgPath;
        readonly string ledDriveCfgPath;
        readonly string ledTamplatePath;
        public readonly string PhecPath;
        public readonly string RCellPath;

        LedUtility ledUtility;

        CLEDSender ledSender = new CLEDSender();
        private const int WM_LED_NOTIFY = 1025;

        public LedService(string rootPath)
        {
            this.rootPath = rootPath;
            ledCfgPath = Path.Combine(rootPath, @"led\ledposition.asoft");
            ledDriveCfgPath = Path.Combine(rootPath, @"led\leddrive.asoft");
            ledTamplatePath = Path.Combine(rootPath, @"led\template.vsq");
            PhecPath = Path.Combine(rootPath, @"led\phec1.bmp");
            RCellPath = Path.Combine(rootPath, @"led\rcell1.bmp");
            ledUtility = new LedUtility();
        }

        #endregion

        #region Serializer
        void SerializerWrite<T>(T obj, string fileName, IEnumerable<Type> knownTypes)
        {
            DataContractSerializer serializer = new DataContractSerializer(typeof(T), knownTypes);
            XmlWriterSettings settings = new XmlWriterSettings { Indent = true, Encoding = Encoding.UTF8 };
            var dirName = Path.GetDirectoryName(fileName);
            if (!Directory.Exists(dirName))
            {
                Directory.CreateDirectory(dirName);
            }
            using (XmlWriter writer = XmlWriter.Create(fileName, settings))
            {
                serializer.WriteObject(writer, obj);
            }
        }

        T SerializerRead<T>(string fileName, IEnumerable<Type> knownTypes)
        {
            T result;
            using (var xmlReader = new XmlTextReader(System.IO.File.Open(fileName, System.IO.FileMode.Open)))
            {
                DataContractSerializer serializer = new DataContractSerializer(typeof(T), knownTypes);
                result = (T)serializer.ReadObject(xmlReader);
            }

            return result;
        }
        #endregion

        #region Cfg & LedPic

        LedCfg ledCfg;
        public LedCfg LedCfg
        {
            get
            {
                if (ledCfg == null)
                {
                    if (!File.Exists(ledCfgPath))
                    {
                        ledCfg = new LedCfg();
                    }
                    else
                    {
                        ledCfg = SerializerRead<LedCfg>(ledCfgPath, null);
                    }
                }
                return ledCfg;
            }
        }

        public void SaveLedCfg()
        {
            if (ledCfg == null)
            {
                return;
            }
            SerializerWrite<LedCfg>(ledCfg, ledCfgPath, null);
        }

        public void SaveLedCfgFromTable()
        {
            if (ledTable == null)
            {
                return;
            }

            var typeLedCfg = typeof(LedCfg);
            ledTable.Rows.Cast<DataRow>().ToList().ForEach(r =>
            {
                var pName = r["PName"].ToString();
                if (!pName.Contains("."))
                {
                    typeLedCfg.GetProperty(pName).SetValue(LedCfg, r["PValue"], null);
                }
                else
                {
                    var ary = pName.Split('.');
                    var sv = typeLedCfg.GetProperty(ary[0]).GetValue(LedCfg, null);
                    sv.GetType().GetProperty(ary[1]).SetValue(sv, r["PValue"], null);
                }
            });
            SaveLedCfg();
        }

        DataTable ledTable;

        public DataTable LedTable
        {
            get
            {
                if (ledTable == null)
                {
                    ledTable = new DataTable();
                    ledTable.Columns.Add("PName");
                    ledTable.Columns.Add("PValue", typeof(int));

                    var ps = LedCfg.GetType().GetProperties().ToList();
                    ps.ForEach(p =>
                    {
                        if (p.PropertyType == typeof(int))
                        {
                            var row = ledTable.NewRow();
                            row[0] = p.Name;
                            row[1] = p.GetValue(LedCfg, null);
                            ledTable.Rows.Add(row);
                        }
                        else
                        {
                            var subType = p.PropertyType;
                            var sv = p.GetValue(LedCfg, null);
                            subType.GetProperties().ToList().ForEach(sp =>
                            {
                                var row = ledTable.NewRow();
                                row[0] = p.Name + "." + sp.Name;
                                row[1] = sp.GetValue(sv, null);
                                ledTable.Rows.Add(row);
                            });
                        }
                    });
                }
                return ledTable;
            }
        }

        LedDriveCfg ledDriveCfg;
        public LedDriveCfg LedDriveCfg
        {
            get
            {
                if (ledDriveCfg == null)
                {
                    if (!File.Exists(ledDriveCfgPath))
                    {
                        ledDriveCfg = new LedDriveCfg();
                        ledDriveCfg.Enabled = false;
                        ledDriveCfg.Ip = "192.168.1.107";
                        ledDriveCfg.Port = 8881;
                        ledDriveCfg.SwitchSecond = 10;
                        ledDriveCfg.Timeo = 100;
                        ledDriveCfg.Repeat = 10;
                    }
                    else
                    {
                        ledDriveCfg = SerializerRead<LedDriveCfg>(ledDriveCfgPath, null);
                    }
                }
                return ledDriveCfg;
            }
        }

        public void SaveLedDriveCfg()
        {
            if (ledDriveCfg == null)
            {
                return;
            }
            SerializerWrite<LedDriveCfg>(ledDriveCfg, ledDriveCfgPath, null);
        }



        LedPic ledPic;
        public LedPic LedPic
        {
            get
            {
                if (ledPic != null)
                {
                    return ledPic;
                }
                ledPic = new LedPic(LedCfg, Path.Combine(rootPath, @"led\"));
                return ledPic;
            }
        }

        #endregion

        #region Start & End & Reload

        public void Start()
        {
            Start(true);
        }

        public void Start(bool sendVSQ)
        {
            Reload();
            if (!LedDriveCfg.Enabled)
            {
                return;
            }
            //强制关闭一下led,避免程序异常后，无法下发LED数据
            //End();
            try
            {
                ledSender.Do_LED_Cleanup();

            }
            catch { }

            ledSender.Do_LED_Startup();

            if (sendVSQ)
            {
                int i = 0;
                while (i < 10)
                {
                    Thread.Sleep(100);
                    try
                    {
                        SendVSQ();
                        //显示历史数据，防止空屏难看
                        //如果延迟Start,可以取消
                        //SendPage1();
                        i = 11;
                    }
                    catch (Exception ex)
                    {
                        i++;
                        if (i == 10)
                        {
                            var ledUtility = new LedUtility();
                            ledUtility.Loger.Trace("10次尝试发送VSQ到LED出错,不再发送");
                            ledUtility.Loger.Trace(ex.Message);
                            throw;
                        }

                        //i++;
                        //if (i == 10)
                        //{
                        //    throw;
                        //}
                        //else
                        //{
                        //    LedDriveCfg.Port = (ushort)(LedDriveCfg.Port - 1);
                        //    SaveLedDriveCfg();
                        //}
                    }
                    finally
                    {
                        ledSender.Do_LED_Cleanup();
                    }
                }
            }

        }



        public void End()
        {
            //if (!LedDriveCfg.Enabled || !IsStart)
            //{
            //    return;
            //}
            //try
            //{
            //    ledSender.Do_LED_Cleanup();

            //}
            //catch (Exception ex)
            //{
            //    (new LedUtility()).Loger.Trace(string.Format("LED END ERROR:{0}", ex.Message));
            //}
            //finally
            //{
            //    IsStart = false;
            //}
        }

        public void Reload()
        {
            ledCfg = null;
            ledDriveCfg = null;
            ledPic = null;
            ledTable = null;
        }

        #endregion

        #region SEND

        public LedPicBase GetLedPic(ELedPic eLedPic)
        {
            LedPicBase result = null;
            var ledPath = rootPath + "led\\";
            switch (eLedPic)
            {
                case ELedPic.Phec:
                    result = new LedPicPhec(LedCfg, ledPath);
                    break;
                case ELedPic.RCell:
                    result = new LedPicRCell(LedCfg, ledPath);
                    break;
                case ELedPic.Smile:
                    result = new LedPicSmile(LedCfg, ledPath);
                    break;
                case ELedPic.Weather:
                    result = new LedPicWeather(LedCfg, ledPath);
                    break;
                default:
                    throw new NotImplementedException();
            }
            return result;
        }

        public void Create(ELedPic eLedPic, params object[] ps)
        {
            var ledPic = GetLedPic(eLedPic);
            ledPic.Create(ps);
        }

        public void SendPhec(List<PhecData> phecDatas)
        {
            LedPic.CreatePhec(phecDatas);
            SendPic(LedCfg.PhecX, ledCfg.PhecY, ledCfg.PhecW, ledCfg.PhecH, PhecPath);

            ledUtility.Loger.Trace(string.Format("send-phec:{0}", string.Join(",", phecDatas.Select(p => p.Name).ToArray())));
        }

        public void SendPage2(WeatherData weatherData, List<PhecData> phecDatas)
        {
            //Send((index) =>
            //{
            //    var ledPicWeather = GetLedPic(ELedPic.Weather);
            //    var ledPicSmile = GetLedPic(ELedPic.Smile);
            //    var ledPicPhec = GetLedPic(ELedPic.Phec);

            //    ledPicWeather.Create(weatherData);
            //    ledPicSmile.Create(null);
            //    ledPicPhec.Create(phecDatas);

            //    ledSender.Do_AddPicture(index, LedCfg.PhecX, ledCfg.PhecY, ledCfg.PhecW, ledCfg.PhecH, ledSender.V_TRUE, 0, ledPicPhec.SendPicPath, 0, 1, 0, 1, 0, 0, 0, 0);
            //    ledSender.Do_AddPicture(index, LedCfg.WeatherX, ledCfg.WeatherY, ledCfg.WeatherW, ledCfg.WeatherH, ledSender.V_TRUE, 0, ledPicWeather.SendPicPath, 0, 1, 0, 1, 0, 0, 0, 0);
            //    ledSender.Do_AddPicture(index, LedCfg.SmileX, ledCfg.SmileY, ledCfg.SmileW, ledCfg.SmileH, ledSender.V_TRUE, 0, ledPicSmile.SendPicPath, 0, 1, 0, 1, 0, 0, 0, 0);

            //});
            if (true)
            {
                var ledPicWeather = GetLedPic(ELedPic.Weather);
                var ledPicSmile = GetLedPic(ELedPic.Smile);
                var ledPicPhec = GetLedPic(ELedPic.Phec);

                ledPicWeather.Create(weatherData);
                ledPicSmile.Create(null);
                ledPicPhec.Create(phecDatas);

                var parms = new List<LedPictureParam>();

                parms.Add(new LedPictureParam(LedCfg.PhecX, ledCfg.PhecY, ledCfg.PhecW, ledCfg.PhecH, ledPicPhec.SendPicPath));
                parms.Add(new LedPictureParam(LedCfg.SmileX, ledCfg.SmileY, ledCfg.SmileW, ledCfg.SmileH, ledPicSmile.SendPicPath));
                parms.Add(new LedPictureParam(LedCfg.WeatherX, ledCfg.WeatherY, ledCfg.WeatherW, ledCfg.WeatherH, ledPicWeather.SendPicPath));

                Send(parms);
            };

        }

        public void SendPage1(WeatherData weatherData, RData rData1, RData rData2, RData rData3)
        {

            if (true)
            {
                var ledPicWeather = GetLedPic(ELedPic.Weather);
                var ledPicRCell = GetLedPic(ELedPic.RCell);

                ledPicWeather.Create(weatherData);
                ledPicRCell.Create(rData1, rData2, rData3);

                var parms = new List<LedPictureParam>();

                parms.Add(new LedPictureParam(LedCfg.RX, ledCfg.RY, ledCfg.RW, ledCfg.RH, ledPicRCell.SendPicPath));
                parms.Add(new LedPictureParam(0, 0, 0, 0, ""));
                parms.Add(new LedPictureParam(LedCfg.WeatherX, ledCfg.WeatherY, ledCfg.WeatherW, ledCfg.WeatherH, ledPicWeather.SendPicPath));

                Send(parms);
            };
        }

        public void SendPage2()
        {
            //Send((index) =>
            //{
            //    var ledPicWeather = GetLedPic(ELedPic.Weather);
            //    var ledPicSmile = GetLedPic(ELedPic.Smile);
            //    var ledPicPhec = GetLedPic(ELedPic.Phec);

            //    ledSender.Do_AddPicture(index, LedCfg.PhecX, ledCfg.PhecY, ledCfg.PhecW, ledCfg.PhecH, ledSender.V_TRUE, 0, ledPicPhec.SendPicPath, 0, 1, 0, 1, 0, 0, 0, 0);
            //    ledSender.Do_AddPicture(index, LedCfg.WeatherX, ledCfg.WeatherY, ledCfg.WeatherW, ledCfg.WeatherH, ledSender.V_TRUE, 0, ledPicWeather.SendPicPath, 0, 1, 0, 1, 0, 0, 0, 0);
            //    ledSender.Do_AddPicture(index, LedCfg.SmileX, ledCfg.SmileY, ledCfg.SmileW, ledCfg.SmileH, ledSender.V_TRUE, 0, ledPicSmile.SendPicPath, 0, 1, 0, 1, 0, 0, 0, 0);

            //});

            if (true)
            {
                var ledPicWeather = GetLedPic(ELedPic.Weather);
                var ledPicSmile = GetLedPic(ELedPic.Smile);
                var ledPicPhec = GetLedPic(ELedPic.Phec);


                var parms = new List<LedPictureParam>();

                parms.Add(new LedPictureParam(LedCfg.PhecX, ledCfg.PhecY, ledCfg.PhecW, ledCfg.PhecH, ledPicPhec.SendPicPath));
                parms.Add(new LedPictureParam(LedCfg.SmileX, ledCfg.SmileY, ledCfg.SmileW, ledCfg.SmileH, ledPicSmile.SendPicPath));
                parms.Add(new LedPictureParam(LedCfg.WeatherX, ledCfg.WeatherY, ledCfg.WeatherW, ledCfg.WeatherH, ledPicWeather.SendPicPath));

                Send(parms);
            };


        }

        public void SendR(WeatherData wData, RData rData1, RData rData2)
        {
            LedPic.CreateRCell(wData, rData1, rData2);
            SendPic(LedCfg.RX, ledCfg.RY, ledCfg.RW, ledCfg.RH, RCellPath);

            ledUtility.Loger.Trace(string.Format("send-room:{0},{1}", rData1 == null ? "" : rData1.Text0, rData2 == null ? "" : rData2.Text0));
        }

        public void SendPage1()
        {
            //Send((index) =>
            //{
            //    var ledPicWeather = GetLedPic(ELedPic.Weather);
            //    var ledPicRCell = GetLedPic(ELedPic.RCell);

            //    ledSender.Do_AddPicture(index, LedCfg.RX, ledCfg.RY, ledCfg.RW, ledCfg.RH, ledSender.V_TRUE, 0, ledPicRCell.SendPicPath, 0, 1, 0, 1, 0, 0, 0, 0);
            //    ledSender.Do_AddPicture(index, LedCfg.WeatherX, ledCfg.WeatherY, ledCfg.WeatherW, ledCfg.WeatherH, ledSender.V_TRUE, 0, ledPicWeather.SendPicPath, 0, 1, 0, 1, 0, 0, 0, 0);

            //});


            if (true)
            {
                var ledPicWeather = GetLedPic(ELedPic.Weather);
                var ledPicRCell = GetLedPic(ELedPic.RCell);

                var parms = new List<LedPictureParam>();

                parms.Add(new LedPictureParam(LedCfg.RX, ledCfg.RY, ledCfg.RW, ledCfg.RH, ledPicRCell.SendPicPath));
                parms.Add(new LedPictureParam(0, 0, 0, 0, ""));
                parms.Add(new LedPictureParam(LedCfg.WeatherX, ledCfg.WeatherY, ledCfg.WeatherW, ledCfg.WeatherH, ledPicWeather.SendPicPath));

                Send(parms);
            };
        }

        void SendPic(int x, int y, int w, int h, string picName)
        {
            TSenderParam param = GetSenderParam();
            OnLedSendMessage("正在执行命令或者发送数据");

            var index = (ushort)ledSender.Do_MakeObject(
                ledSender.ROOT_PLAY_OBJECT,
                ledSender.ACTMODE_REPLACE,
                0, 0, 0, 0,
                ledSender.COLOR_MODE_DOUBLE);
            ledSender.Do_AddPicture(index, x, y, w, h, ledSender.V_TRUE, 0, picName, 0, 1, 0, 1, 0, 0, 0, 0);
            SendToScreen(param, index);
        }

        void Send(List<LedPictureParam> pictureParams)
        {
            try
            {
                var message = CheckResult(ledSender.Do_LED_Startup());
                ledUtility.Loger.Trace(string.Format("{0}:{1}", "LED_Startup", message));
                Thread.Sleep(200);
                TSenderParam param = GetSenderParam();
                OnLedSendMessage("正在执行命令或者发送数据");

                foreach (var pp in pictureParams)
                {
                    ledUtility.Loger.Trace(pp.PictureName);
                }

                ////测试，每次都发送VSQ文件
                //SendVSQ(param);

                for (int i = 0; i < pictureParams.Count; i++)
                {
                    var pp = pictureParams[i];
                    var index = (ushort)ledSender.Do_MakeObject(
                        ledSender.ROOT_PLAY_OBJECT,
                        ledSender.ACTMODE_REPLACE,
                        0, 0, 0, i,
                        ledSender.COLOR_MODE_DOUBLE);
                    ledSender.Do_AddPicture(index, pp.X, pp.Y, pp.W, pp.H, ledSender.V_TRUE, 0, pp.PictureName, 0, 1, 0, 1, 0, 0, 0, 0);
                    SendToScreen(param, index);
                }
            }
            catch
            {
                throw;
            }
            finally
            {
                Thread.Sleep(200);
                //try
                //{
                //    var message = ledSender.Do_LED_Cleanup();
                //    //var message = CheckResult(ledSender.Do_LED_Cleanup());
                //    ledUtility.Loger.Trace(string.Format("{0}:{1}", "LED_Cleanup", message));
                //}
                //catch (Exception ex)
                //{
                //    ledUtility.Loger.Error(ex.ToString());
                //}
            }

        }

        void Send(Action<ushort> addPictures)
        {
            TSenderParam param = GetSenderParam();
            OnLedSendMessage("正在执行命令或者发送数据");
            var index = (ushort)ledSender.Do_MakeObject(
                ledSender.ROOT_PLAY_OBJECT,
                ledSender.ACTMODE_REPLACE,
                0, 0, 0, 0,
                ledSender.COLOR_MODE_DOUBLE);
            addPictures(index);
            SendToScreen(param, index);
        }

        public void SendVSQ()
        {
            TSenderParam param = GetSenderParam();
            OnLedSendMessage("正在执行命令或者发送数据");
            param = SendVSQ(param);
        }

        private TSenderParam SendVSQ(TSenderParam param)
        {
            var index = (ushort)ledSender.Do_MakeFromVsqFile(
                ledTamplatePath,
                ledSender.ROOT_PLAY,
                ledSender.COLOR_MODE_DOUBLE,
                ledSender.SURVIVE_ALWAYS);
            SendToScreen(param, index);
            return param;
        }



        void SendToScreen(TSenderParam param, int index)
        {

            var message = "";
            for (int i = 0; i < 5; i++)
            {
                message = CheckResult(ledSender.Do_LED_SendToScreen(ref param, index));
                (new LedUtility()).Loger.Trace(string.Format("LED MESSAGE:{0}", message));
                Thread.Sleep(100);
                if (message != "命令执行超时")
                {
                    break;
                }
            }
            OnLedSendMessage(message);
        }

        TSenderParam GetSenderParam()
        {
            TSenderParam param = new TSenderParam();

            param.devParam.devType = ledSender.DEVICE_TYPE_UDP;
            param.devParam.comPort = 0;
            param.devParam.comSpeed = 0;
            param.devParam.locPort = LedDriveCfg.Port;
            param.devParam.rmtHost = LedDriveCfg.Ip;
            param.devParam.rmtPort = 6666;
            param.devParam.dstAddr = 0;
            param.devParam.txTimeo = LedDriveCfg.Timeo;
            param.devParam.txRepeat = ledDriveCfg.Repeat;

            param.notifyMode = ledSender.NOTIFY_BLOCK;
            //            param.wmHandle = (UInt32)Handle;
            param.wmMessage = (UInt32)WM_LED_NOTIFY;
            return param;
        }

        private string CheckResult(int resultNumber)
        {
            TNotifyParam notifyparam = new TNotifyParam();
            if (resultNumber >= 0)
            {
                ledSender.Do_LED_GetNotifyParam(ref notifyparam, resultNumber);

                if (notifyparam.notify == ledSender.LM_TIMEOUT)
                {
                    return "命令执行超时";
                }
                else if (notifyparam.notify == ledSender.LM_TX_COMPLETE)
                {
                    if (notifyparam.result == ledSender.RESULT_FLASH)
                    {
                        return "数据传送完成，正在写入Flash";
                    }
                    else
                    {
                        return "数据传送完成";
                    }
                }
                else if (notifyparam.notify == ledSender.LM_RESPOND)
                {
                    if (notifyparam.command == ledSender.PKC_GET_POWER)
                    {
                        if (notifyparam.status == ledSender.LED_POWER_ON)
                            return "读取电源状态完成，当前为电源开启状态";
                        else if (notifyparam.status == ledSender.LED_POWER_OFF)
                            return "读取电源状态完成，当前为电源关闭状态";
                    }
                    else if (notifyparam.command == ledSender.PKC_SET_POWER)
                    {
                        if (notifyparam.result == 99)
                            return "当前为定时开关屏模式";
                        else if (notifyparam.status == ledSender.LED_POWER_ON)
                            return "设置电源状态完成，当前为电源开启状态";
                        else
                            return "设置电源状态完成，当前为电源关闭状态";
                    }
                    else if (notifyparam.command == ledSender.PKC_GET_BRIGHT)
                    {
                        return string.Format("读取亮度完成，当前亮度={0:D}", notifyparam.status);
                    }
                    else if (notifyparam.command == ledSender.PKC_SET_BRIGHT)
                    {
                        if (notifyparam.result == 99)
                            return "当前为定时亮度调节模式";
                        else
                            return string.Format("设置亮度完成，当前亮度={0:D}", notifyparam.status);
                    }
                    else if (notifyparam.command == ledSender.PKC_ADJUST_TIME)
                    {
                        return "校正显示屏时间完成";
                    }
                }
            }
            else if (resultNumber == ledSender.R_DEVICE_INVALID)
            {
                return "打开通讯设备失败(网络端口被占用)";
            }
            else if (resultNumber == ledSender.R_DEVICE_BUSY)
            {
                return "设备忙，正在通讯中...";
            }

            return "未知";
        }

        #endregion

        #region event

        public event EventHandler<LedSendMessageArgs> LedSendMessage;

        void OnLedSendMessage(string message)
        {
            if (LedSendMessage != null)
            {
                LedSendMessage(this, new LedSendMessageArgs(message));
            }
        }

        #endregion

    }

    public class LedSendMessageArgs : EventArgs
    {
        public LedSendMessageArgs(string message)
        {
            this.Message = message;
        }

        public string Message { get; private set; }
    }

    public class LedPictureParam
    {
        public LedPictureParam(int x, int y, int w, int h, string pictureName)
        {
            this.X = x;
            this.Y = y;
            this.W = w;
            this.H = h;
            this.PictureName = pictureName;
        }

        public int X { get; private set; }

        public int Y { get; private set; }

        public int W { get; private set; }

        public int H { get; private set; }

        public string PictureName { get; private set; }

    }


}
